﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Acms.DatabaseTest.Helpers;
using System.Data;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Acms.DatabaseTest
{
    [TestClass]
    public class PostPositionTests
    {
        [ClassInitialize]
        public static void Init(TestContext context)
        {
            TestConnection.Clean();
        }

        [TestMethod]
        public void BuildTreeTest1()
        {
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site", "Root", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);

                //create tree of 10 posts all parent > child
                Guid currentGuid = Guid.Empty;
                Guid parentGuid = Guid.Empty;
                Enumerable.Range(1, 10).ToList().ForEach(i =>
                {
                    parentGuid = currentGuid;
                    currentGuid = Guid.NewGuid();
                    conn.Post_Add(i, 1, currentGuid, parentGuid, i == 1 ? "~" : "Post" + i, "Post At " + i, 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                });

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 10 post position records with correct paths and parent sequence records
                var results = conn.PostPosition_GetAll();

                Assert.AreEqual(10, results.Count);
                string expectedSequence = "000";
                string expectedParentPath = "";
                var n = 0;
                foreach (var r in results.OrderBy(x => x["ParentSequence"]))
                {
                    Assert.AreEqual(n + 1, r["PostId"]);
                    if (n == 0)
                    {
                        Assert.IsNull(r["ParentPath"]);
                    }
                    else
                    {
                        Assert.AreEqual(expectedParentPath, r["ParentPath"]);
                    }

                    Assert.AreEqual(expectedSequence, r["ParentSequence"]);

                    if (n == 0)
                    {
                        expectedParentPath = "~/";
                    }
                    else
                    {
                        expectedParentPath += "Post" + (n + 1) + "/";
                    }
                    expectedSequence += "_000";
                    n++;

                }
            }
        }

        [TestMethod]
        public void BuildTreeMultipleSitesTest()
        {
            //test against multiple sites in db
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site1", "Root1", true, true, false);
                conn.Site_Add(2, "Site2", "Root2", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);
                conn.Template_Add(2, 2, "Name", "Notes", 1, false, false);

                //create tree of 10 posts all parent > child
                Guid currentGuid1 = Guid.Empty;
                Guid currentGuid2 = Guid.Empty;
                Guid parentGuid1 = Guid.Empty;
                Guid parentGuid2 = Guid.Empty;
                Enumerable.Range(1, 10).ToList().ForEach(i =>
                {
                    parentGuid1 = currentGuid1;
                    currentGuid1 = Guid.NewGuid();
                    conn.Post_Add((i * 2) - 1, 1, currentGuid1, parentGuid1, i == 1 ? "~" : "Post" + i, "Post At " + i, 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);

                    parentGuid2 = currentGuid2;
                    currentGuid2 = Guid.NewGuid();
                    conn.Post_Add((i * 2), 2, currentGuid2, parentGuid2, i == 1 ? "~" : "Post" + i, "Post At " + i, 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                });

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 10 post position records with correct paths and parent sequence records
                var results = conn.PostPosition_GetAll();

                Assert.AreEqual(10, results.Count);
                string expectedSequence = "000";
                string expectedParentPath = "";
                var n = 0;
                foreach (var r in results.OrderBy(x => x["ParentSequence"]))
                {
                    Assert.AreEqual((n * 2) + 1, r["PostId"]);
                    if (n == 0)
                    {
                        Assert.IsNull(r["ParentPath"]);
                    }
                    else
                    {
                        Assert.AreEqual(expectedParentPath, r["ParentPath"]);
                    }

                    Assert.AreEqual(expectedSequence, r["ParentSequence"]);

                    if (n == 0)
                    {
                        expectedParentPath = "~/";
                    }
                    else
                    {
                        expectedParentPath += "Post" + (n + 1) + "/";
                    }
                    expectedSequence += "_000";
                    n++;

                }

                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 2)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 20 post position records with correct paths and parent sequence records
                results = conn.PostPosition_GetAll();
                Assert.AreEqual(20, results.Count);
            }
        }

        [TestMethod]
        public void BuildTreeAddedPostTest()
        {
            //added new post and build tree from that post
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site1", "Root1", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);

                //create tree of 10 posts all parent > child
                Guid currentGuid = Guid.Empty;
                Guid parentGuid = Guid.Empty;
                Enumerable.Range(1, 10).ToList().ForEach(i =>
                {
                    parentGuid = currentGuid;
                    currentGuid = Guid.NewGuid();
                    conn.Post_Add(i, 1, currentGuid, parentGuid, i == 1 ? "~" : "Post" + i, "Post At " + i, 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                });

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 10 post position records with correct paths and parent sequence records
                var results = conn.PostPosition_GetAll();
                Assert.AreEqual(10, results.Count);
                Assert.AreEqual("~/Post2/Post3/Post4/Post5/Post6/Post7/Post8/Post9/", results.Last()["ParentPath"]);
                Assert.AreEqual("000_000_000_000_000_000_000_000_000_000", results.Last()["ParentSequence"]);

                //Now add new post and call build tree

                parentGuid = currentGuid;
                currentGuid = Guid.NewGuid();
                conn.Post_Add(11, 1, currentGuid, parentGuid, "Post 11", "Post At 11", 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, parentGuid)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 11 post position records with correct paths and parent sequence records
                results = conn.PostPosition_GetAll();
                Assert.AreEqual(11, results.Count);
                Assert.AreEqual("~/Post2/Post3/Post4/Post5/Post6/Post7/Post8/Post9/Post10/", results.Last()["ParentPath"]);
                Assert.AreEqual("000_000_000_000_000_000_000_000_000_000_000", results.Last()["ParentSequence"]);
            }
        }

        [TestMethod]
        public void BuildTreeDeletedPostTest()
        {
            //added new post and build tree from that post
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site1", "Root1", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);

                //create tree of 10 posts all parent > child
                Guid currentGuid = Guid.Empty;
                Guid parentGuid = Guid.Empty;
                Enumerable.Range(1, 10).ToList().ForEach(i =>
                {
                    parentGuid = currentGuid;
                    currentGuid = Guid.NewGuid();
                    conn.Post_Add(i, 1, currentGuid, parentGuid, i == 1 ? "~" : "Post" + i, "Post At " + i, 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                });

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 10 post position records with correct paths and parent sequence records
                Assert.AreEqual(10, conn.PostPosition_GetAll().Count);

                //Now add delete the 5th post and build tree
                conn.Execute("Update acms.Posts set deleted = 1 where id = 5");

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );
                //Should have 4 post position records with correct paths and parent sequence records
                var results = conn.PostPosition_GetAll();
                Assert.AreEqual(4, results.Count);
            }
        }

        [TestMethod]
        public void BuildTreeDeletedPostTest2()
        {
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site1", "Root1", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);

                //create tree of 10 posts all parent > child
                Guid currentGuid = Guid.Empty;
                Guid parentGuid = Guid.Empty;
                Enumerable.Range(1, 10).ToList().ForEach(i =>
                {
                    parentGuid = currentGuid;
                    currentGuid = Guid.NewGuid();
                    conn.Post_Add(i, 1, currentGuid, parentGuid, i == 1 ? "~" : "Post" + i, "Post At " + i, 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                });

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 10 post position records with correct paths and parent sequence records
                Assert.AreEqual(10, conn.PostPosition_GetAll().Count);

                var deletedPostParentGuid = (Guid)conn.Post_Get(5)["ParentGuid"];

                //Now add delete the 5th post and build tree
                conn.Execute("Update acms.Posts set deleted = 1 where id = 5");

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, deletedPostParentGuid)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );
                //Should have 4 post position records with correct paths and parent sequence records
                var results = conn.PostPosition_GetAll();
                Assert.AreEqual(4, results.Count);
            }
        }

        [TestMethod]
        public void BuildTreeRenamePostTest()
        {
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site1", "Root1", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);

                //create tree of 10 posts all parent > child
                Guid currentGuid = Guid.Empty;
                Guid parentGuid = Guid.Empty;
                Enumerable.Range(1, 10).ToList().ForEach(i =>
                {
                    parentGuid = currentGuid;
                    currentGuid = Guid.NewGuid();
                    conn.Post_Add(i, 1, currentGuid, parentGuid, i == 1 ? "~" : "Post" + i, "Post At " + i, 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                });

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, null)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //Should have 10 post position records with correct paths and parent sequence records
                Assert.AreEqual(10, conn.PostPosition_GetAll().Count);

                var renamedPostGuid = (Guid)conn.Post_Get(5)["PostGuid"];

                //Now add delete the 5th post and build tree
                conn.Execute("Update acms.Posts set Name = 'Renamed' where id = 5");

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, renamedPostGuid)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );
                //Should have 10 post position records with correct paths and parent sequence records
                var results = conn.PostPosition_GetAll();
                Assert.AreEqual(10, results.Count);

                var n = 1;
                var expectedparentPath = "~/";
                foreach (var r in results.OrderBy(x => x["ParentSequence"]).Skip(1))
                {
                    Assert.AreEqual(expectedparentPath, r["ParentPath"]);
                    if (n == 4)
                    {
                        expectedparentPath += "Renamed/";
                    }
                    else
                    {
                        expectedparentPath += "Post" + (n + 1) + "/";
                    }
                    n++;
                }
            }
        }

        [TestMethod]
        public void BuildTreeRenameTreePostTest()
        {
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site1", "Root1", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);

                Guid rootGuid = Guid.NewGuid();
                //add site root
                var postID = 1;
                conn.Post_Add(postID, 1, rootGuid, Guid.Empty, "~", "home", 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                //build three level tree
                for (int i = 1; i < 4; i++)
                {
                    postID++;
                    Guid level1Guid = Guid.NewGuid();
                    conn.Post_Add(postID, 1, level1Guid, rootGuid, "child" + i, "child " + i, i, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                    for (int n = 1; n < 4; n++)
                    {
                        postID++;
                        Guid level2Guid = Guid.NewGuid();
                        conn.Post_Add(postID, 1, level2Guid, level1Guid, "child" + i + "_child" + n, "child " + i + " - child" + n, n, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                    }
                }

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, rootGuid)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                Assert.AreEqual(13, conn.PostPosition_GetAll().Count);

                //Rename child 1 and rebuild tree
                var rebuildGuid = conn.Post_Get(2)["PostGuid"];
                conn.Execute("update acms.posts set name = 'renamed' where id = 2");

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, rebuildGuid)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //all children of child 1 should be renamed
                var expectedParentPaths = new object[]{
                    null,
                    "~/",
                    "~/renamed/",
                    "~/renamed/",
                    "~/renamed/",
                    "~/",
                    "~/child2/",
                    "~/child2/",
                    "~/child2/",
                    "~/",
                    "~/child3/",
                    "~/child3/",
                    "~/child3/",
                };

                var results = conn.PostPosition_GetAll().OrderBy(x => x["ParentSequence"]).ToList();
                Assert.AreEqual(13, results.Count);
                for (int i = 0; i < 13; i++)
                {
                    Assert.AreEqual(expectedParentPaths[i], results[i]["ParentPath"], "Failed @ Position " + i);
                }
            }
        }
        
        [TestMethod]
        public void BuidTreeFromNewPost()
        {
            using (var conn = TestConnection.Begin())
            {
                //create site
                conn.Site_Add(1, "Site1", "Root1", true, true, false);

                //create template
                conn.Template_Add(1, 1, "Name", "Notes", 1, false, false);

                var rootGuid = Guid.NewGuid();
                var childGuid = Guid.NewGuid();

                //create root posts and post position
                conn.Post_Add(1, 1, rootGuid, Guid.Empty, "~", "Root", 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);
                //add the post posisiont
                conn.PostPosition_Add(Guid.NewGuid(), 1, null, null, null, "000");

                //create a child post 
                conn.Post_Add(2, 1, childGuid, rootGuid, "Child", "Child", 0, 1, null, null, DateTime.Now, null, null, null, false, false, false, false, null, null);

                //call build tree
                conn.ExecuteProc("[acms].[PostPosition_BuildTree]",
                    new ParamCollection()
                        .AddInputParam("SiteId", SqlDbType.Int, 1)
                        .AddInputParam("ParentGuid", SqlDbType.UniqueIdentifier, childGuid)
                        .AddInputParam("PublishDate", SqlDbType.DateTime, null)
                    );

                //check there are two post positions and settings for second are correct
                var results = conn.PostPosition_GetAll();
                Assert.AreEqual(2, results.Count);

                Assert.AreEqual(2, results[1]["PostId"]);
                Assert.AreEqual("~/", results[1]["ParentPath"]);

            }
        }
    }
}
